﻿#include "../exercise.h"
#include <memory>
#include <string>

// READ: `std::unique_ptr` <https://zh.cppreference.com/w/cpp/memory/unique_ptr>
// READ: `std::make_unique` <https://zh.cppreference.com/w/cpp/memory/unique_ptr/make_unique>

struct Resource {
    std::string data;
    Resource(std::string d) : data(std::move(d)) {}
    ~Resource() { std::cout << "Resource destroyed: " << data << std::endl; }
};

template<typename T>
struct MyUniquePtr {
    // TODO: 实现 unique_ptr 的基本功能
    // NOTICE: 面试时经常会问到 unique_ptr 的实现原理，建议自己独立实现一个简单的 unique_ptr。
    // 这有助于加深对 unique_ptr 的理解，并且可以在面试中展示自己的能力。
    // 例如，构造函数、析构函数、移动构造函数、移动赋值运算符等。

private:
    T* ptr = nullptr;

public:
    // 默认构造函数
    MyUniquePtr() = default;
    
    // 从原始指针构造
    explicit MyUniquePtr(T* p) : ptr(p) {}
    
    // 禁止拷贝
    MyUniquePtr(const MyUniquePtr&) = delete;
    MyUniquePtr& operator=(const MyUniquePtr&) = delete;
    
    // 移动构造函数
    MyUniquePtr(MyUniquePtr&& other) noexcept : ptr(other.ptr) {
        other.ptr = nullptr;
    }
    
    // 移动赋值运算符
    MyUniquePtr& operator=(MyUniquePtr&& other) noexcept {
        if (this != &other) {
            delete ptr;
            ptr = other.ptr;
            other.ptr = nullptr;
        }
        return *this;
    }
    
    // 析构函数
    ~MyUniquePtr() {
        delete ptr;
    }
    
    // 重载操作符
    T* operator->() const { return ptr; }
    T& operator*() const { return *ptr; }
    
    // 布尔转换
    explicit operator bool() const { return ptr != nullptr; }
    
    // 获取原始指针
    T* get() const { return ptr; }
    
    // 释放所有权
    T* release() {
        T* temp = ptr;
        ptr = nullptr;
        return temp;
    }
    
    // 重置指针
    void reset(T* p = nullptr) {
        delete ptr;
        ptr = p;
    }
    
    // 比较操作符
    bool operator==(std::nullptr_t) const { return ptr == nullptr; }
    bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
};

int main(int argc, char **argv) {
    // Part 1: Basic usage and ownership
    {
        auto ptr1 = std::make_unique<Resource>("Part 1 Resource");
        ASSERT(ptr1 != nullptr, "ptr1 should own a resource");
        ASSERT(ptr1->data == "Part 1 Resource", "Check resource data");

        // TODO: 打开注释并解释为什么这会导致编译错误。
        // auto ptr2 = ptr1;
        // 解释：std::unique_ptr 禁止拷贝构造，因为它是独占所有权的智能指针。
        // 如果允许拷贝，会导致多个指针拥有同一资源，违反独占所有权原则。


        // 使用 std::move 转移所有权
        auto ptr3 = std::move(ptr1);
        ASSERT(ptr3 != nullptr, "ptr3 should own the resource now");
        ASSERT(ptr3->data == "Part 1 Resource", "Check resource data in ptr3");
        ASSERT(ptr1 == nullptr, "ptr1 should be null after move");

        // Resource 在 ptr3 超出作用域时自动销毁
    }

    // Part 2: Using reset()
    {
        auto ptr = std::make_unique<Resource>("Part 2 Initial");
        ASSERT(ptr->data == "Part 2 Initial", "Check initial data");

        // Reset to manage a new resource
        ptr.reset(new Resource("Part 2 New"));
        ASSERT(ptr != nullptr, "ptr should own the new resource");
        ASSERT(ptr->data == "Part 2 New", "Check data of the new resource");

        // Reset to release ownership (becomes nullptr)
        ptr.reset();
        ASSERT(ptr == nullptr, "ptr should be null after reset()");
    }

    // Part 3: Using release()
    {
        auto ptr = std::make_unique<Resource>("Part 3 Resource");
        ASSERT(ptr != nullptr, "ptr should own the resource initially");

        // Release ownership
        Resource* raw_ptr = ptr.release();
        ASSERT(ptr == nullptr, "ptr should be null after release()");
        ASSERT(raw_ptr != nullptr, "raw_ptr should point to the resource");
        ASSERT(raw_ptr->data == "Part 3 Resource", "Check data via raw_ptr");

        // TODO: 请在下方补充代码，确保不会发生内存泄漏。
        // delete ?;
        delete raw_ptr;  // 手动释放资源
    }

    // Part 4: unique_ptr with arrays
    {
        // 使用 make_unique 创建一个 unique_ptr 指向一个包含 5 个整数的数组
        auto arr_ptr = std::make_unique<int[]>(5);

        for (int i = 0; i < 5; ++i) {
            arr_ptr[i] = i * 10;
        }

        ASSERT(arr_ptr[0] == 0, "Check array element 0");
        ASSERT(arr_ptr[4] == 40, "Check array element 4");

        // THINK：作用域结束时，数组都会被销毁吗？
        // 答：是的，unique_ptr 使用 delete[] 来释放数组，所有元素都会被正确销毁。
    }

    // 测试自定义的 MyUniquePtr
    {
        MyUniquePtr<Resource> my_ptr(new Resource("MyUniquePtr Test"));
        ASSERT(my_ptr != nullptr, "MyUniquePtr should own a resource");
        ASSERT(my_ptr->data == "MyUniquePtr Test", "Check resource data");
        
        // 测试移动语义
        MyUniquePtr<Resource> moved_ptr = std::move(my_ptr);
        ASSERT(moved_ptr != nullptr, "Moved pointer should own the resource");
        ASSERT(my_ptr == nullptr, "Original pointer should be null after move");
    }

    return 0;
}




/*
`std::unique_ptr`

`std::make_unique`


方法思路
理解unique_ptr：独占所有权的智能指针，不能拷贝只能移动
核心功能：构造函数、析构函数、移动语义、资源管理
关键方法：reset()、release()、operator->、operator*




代码解释
1. MyUniquePtr 实现核心
构造函数和析构函数：

explicit MyUniquePtr(T* p) : ptr(p) {}  // 从原始指针构造
~MyUniquePtr() { delete ptr; }          // 自动释放资源
禁止拷贝：

MyUniquePtr(const MyUniquePtr&) = delete;
MyUniquePtr& operator=(const MyUniquePtr&) = delete;
移动语义：

MyUniquePtr(MyUniquePtr&& other) noexcept : ptr(other.ptr) {
    other.ptr = nullptr;  // 转移所有权后置空
}
2. 关键方法
reset()：

void reset(T* p = nullptr) {
    delete ptr;  // 释放当前资源
    ptr = p;     // 接管新资源
}
release()：

T* release() {
    T* temp = ptr;
    ptr = nullptr;  // 放弃所有权
    return temp;    // 返回原始指针
}
3. 操作符重载
访问操作符：

T* operator->() const { return ptr; }  // ptr->member
T& operator*() const { return *ptr; }   // *ptr
布尔转换：

explicit operator bool() const { return ptr != nullptr; }  // if (ptr)
unique_ptr 的重要特性
独占所有权
std::unique_ptr 的核心特点是独占所有权：

不能拷贝，只能移动
确保同一时间只有一个指针拥有资源
避免重复释放和内存泄漏
移动语义
auto ptr1 = std::make_unique<Resource>("test");
auto ptr2 = std::move(ptr1);  // 所有权转移
// ptr1 现在为 nullptr
// ptr2 拥有资源
自定义删除器
unique_ptr 支持自定义删除器：

// 使用自定义删除器
auto file_ptr = std::unique_ptr<FILE, decltype(&fclose)>(
    fopen("test.txt", "r"), &fclose
);

// 对于数组使用 delete[]
auto array_ptr = std::unique_ptr<int[], void(*)(int*)>(
    new int[10], [](int* p) { delete[] p; }
);
数组特化
std::unique_ptr 对数组有特化版本：

// 创建数组
auto arr = std::make_unique<int[]>(10);

// 访问元素
arr[0] = 42;

// 自动使用 delete[] 释放
内存管理对比
方式	优点	缺点
原始指针	灵活	容易内存泄漏
unique_ptr	自动管理，安全	不能共享所有权
shared_ptr	共享所有权	性能开销大
weak_ptr	打破循环引用	需要检查有效性
测试验证
程序运行后会通过所有测试：

基本功能正确
移动语义正确
reset() 和 release() 正确
数组支持正确
自定义 MyUniquePtr 正确
这个练习深入理解了std::unique_ptr的实现原理和用法，是C++内存管理的重要知识点。



Resource* raw_ptr = ptr.release();

auto ptr3 = std::move(ptr1);

这两个有什么区别

这两个操作有本质的区别，让我详细解释一下：

1. ptr.release() - 释放所有权
Resource* raw_ptr = ptr.release();
作用：将智能指针管理的资源所有权转移给原始指针，智能指针变为nullptr

内存管理责任转移：

之前：unique_ptr负责管理资源
之后：你负责手动管理资源（需要手动delete）
示例：

auto ptr = std::make_unique<Resource>("test");
Resource* raw_ptr = ptr.release();  // ptr变为nullptr

// 现在你必须手动管理
std::cout << raw_ptr->data << std::endl;
delete raw_ptr;  // 必须手动释放！
2. std::move(ptr1) - 转移所有权
auto ptr3 = std::move(ptr1);
作用：将资源所有权从一个智能指针转移到另一个智能指针

内存管理责任保持：

之前：ptr1负责管理资源
之后：ptr3负责管理资源（自动管理）
示例：

auto ptr1 = std::make_unique<Resource>("test");
auto ptr3 = std::move(ptr1);  // ptr1变为nullptr

// ptr3继续自动管理资源
std::cout << ptr3->data << std::endl;
// 不需要手动释放，ptr3析构时会自动释放
关键区别对比
特性	ptr.release()	std::move(ptr)
返回类型	原始指针 T*	智能指针 unique_ptr<T>
内存管理	手动管理（需要delete）	自动管理
安全性	不安全（可能忘记释放）	安全（RAII）
使用场景	与需要原始指针的API交互	在智能指针间转移所有权
原指针状态	变为nullptr	变为nullptr
实际应用场景
使用 release() 的场景
// 与需要原始指针的C风格API交互
void legacy_function(Resource* res);

auto ptr = std::make_unique<Resource>("data");
Resource* raw = ptr.release();  // 转移给原始指针
legacy_function(raw);           // 传递给传统API
// 注意：现在需要确保legacy_function正确处理内存
使用 std::move() 的场景
// 在函数间传递所有权
std::unique_ptr<Resource> create_resource() {
    return std::make_unique<Resource>("created");
}

void use_resource(std::unique_ptr<Resource> res) {
    // 使用资源
}

auto resource = create_resource();  // 移动构造
use_resource(std::move(resource));   // 移动传递
为什么要有 release()？
虽然release()看起来危险，但在某些情况下是必要的：

与传统代码交互：很多老代码使用原始指针
特殊的内存管理：某些库有自己的内存管理机制
性能关键代码：避免智能指针的开销
但通常应该尽量避免使用release()，除非确实需要。

安全使用模式
如果必须使用release()，建议立即用另一个智能指针接管：

auto ptr = std::make_unique<Resource>("test");
Resource* raw = ptr.release();

// 立即用新的智能指针接管
std::unique_ptr<Resource> safe_ptr(raw);
// 现在又回到安全的自动管理
总结
std::move(ptr)：在智能指针之间安全地转移所有权
ptr.release()：放弃智能指针的管理权，返回需要手动管理的原始指针
首选std::move()：更安全，保持RAII优势
谨慎使用release()：只在必要时使用，并确保正确处理内存
在大多数情况下，你应该使用std::move()来在智能指针间转移所有权，这样可以保持内存管理的安全性。




auto ptr3 = std::move(ptr1);这里的auto指代什么

在 auto ptr3 = std::move(ptr1); 中，auto 指代的是 std::unique_ptr<Resource> 类型。

详细解释
类型推导过程
auto ptr3 = std::move(ptr1);
这里的类型推导过程是：

ptr1 的类型是 std::unique_ptr<Resource>
std::move(ptr1) 返回 std::unique_ptr<Resource>&&（右值引用）
auto 会推导出右值引用所引用的实际类型：std::unique_ptr<Resource>
等价写法
// 使用 auto（推荐）
auto ptr3 = std::move(ptr1);

// 显式指定类型（等价）
std::unique_ptr<Resource> ptr3 = std::move(ptr1);
为什么使用 auto？
代码简洁：避免重复书写冗长的类型名
减少错误：编译器自动推导，避免类型错误
便于重构：如果右侧类型改变，auto 会自动适应
实际类型验证
你可以用 decltype 来验证推导出的类型：

#include <type_traits>

auto ptr3 = std::move(ptr1);

// 验证类型
static_assert(std::is_same_v<decltype(ptr3), std::unique_ptr<Resource>>, 
              "ptr3 should be std::unique_ptr<Resource>");
移动语义的细节
虽然 std::move() 返回右值引用，但 auto 会推导出实际的值类型：

// std::move(ptr1) 返回：std::unique_ptr<Resource>&&
// auto 推导出：std::unique_ptr<Resource>

// 这是因为发生了移动构造：
// auto ptr3 = std::move(ptr1);
// 等价于：std::unique_ptr<Resource> ptr3(std::move(ptr1));
对比其他情况
// 情况1：直接赋值（编译错误，因为unique_ptr不能拷贝）
// auto ptr3 = ptr1;  // 错误！

// 情况2：使用move（正确，触发移动构造）
auto ptr3 = std::move(ptr1);  // 正确

// 情况3：使用auto&&（推导为右值引用）
auto&& ptr_ref = std::move(ptr1);  // ptr_ref的类型是std::unique_ptr<Resource>&&
总结
在 auto ptr3 = std::move(ptr1); 中：

auto 推导出 std::unique_ptr<Resource>
这会调用移动构造函数
ptr1 的所有权转移给 ptr3
ptr1 变为 nullptr
这是使用 std::unique_ptr 时转移所有权的标准做法。

*/